package repository

import (
	"context"
	"database/sql"
	"github.com/ecodeclub/ekit/slice"
	"golang.org/x/sync/errgroup"
	"jk-time/webook/comment/dao"
	"jk-time/webook/comment/domain"
	"time"
)

type CommentRepository interface {
	// FindByBiz 根据 ID 倒序查找
	// 并且会返回每个评论的三条直接回复
	FindByBiz(ctx context.Context, biz string,
		bizId, minID, limit int64) ([]domain.Comment, error)
	// DeleteComment 删除评论，删除本评论何其子评论
	DeleteComment(ctx context.Context, comment domain.Comment) error
	// CreateComment 创建评论
	CreateComment(ctx context.Context, comment domain.Comment) error
	// GetCommentByIds 获取单条评论 支持批量获取
	GetCommentByIds(ctx context.Context, id []int64) ([]domain.Comment, error)
	GetMoreReplies(ctx context.Context, rid int64, maxId, limit int) ([]domain.Comment, error)
}
type commentRepository struct {
	dao dao.CommentDao
}

func NewCommentRepository(dao dao.CommentDao) CommentRepository {
	return &commentRepository{dao: dao}
}

// FindByBiz 并且会返回每个评论的前三条直接回复
func (c *commentRepository) FindByBiz(ctx context.Context, biz string,
	bizId, minID, limit int64) ([]domain.Comment, error) {
	// 缓存第一页，也可以定时刷新缓存
	// 拿到顶级评论
	dComment, err := c.dao.FindByBiz(ctx, biz, bizId, minID, limit)
	if err != nil {
		return nil, err
	}
	//只找每天评论的前三条
	res := make([]domain.Comment, len(dComment))
	downgrade := ctx.Value("downgrade") == "true"

	var eg errgroup.Group
	for i, comment := range dComment {
		dc := comment
		res[i] = c.toDomain(dc)
		if downgrade {
			continue
		}
		eg.Go(func() error {
			subComments, err := c.dao.FindRepliesByPid(ctx, dc.Id, 0, 3)
			if err != nil {
				return err
			}
			res[i].Children = make([]domain.Comment, len(subComments))
			for i2, subComment := range subComments {
				res[i].Children[i2] = c.toDomain(subComment)
			}
			return nil
		})
	}
	return res, eg.Wait()
}
func (c *commentRepository) GetMoreReplies(ctx context.Context, rid int64, maxId int,
	limit int) ([]domain.Comment, error) {
	dc, err := c.dao.FindRepliesByRid(ctx, rid, maxId, limit)
	return slice.Map(dc, func(idx int, src dao.Comment) domain.Comment {
		return c.toDomain(src)
	}), err
}
func (c *commentRepository) CreateComment(ctx context.Context, comment domain.Comment) error {
	return c.dao.Insert(ctx, c.toEntity(comment))
}
func (c *commentRepository) DeleteComment(ctx context.Context, comment domain.Comment) error {
	return c.dao.Delete(ctx, c.toEntity(comment))
}
func (c *commentRepository) GetCommentByIds(ctx context.Context, ids []int64) ([]domain.Comment, error) {
	dComment, err := c.dao.GetCommentByIds(ctx, ids)
	return slice.Map(dComment, func(idx int, src dao.Comment) domain.Comment {
		return c.toDomain(src)
	}), err
}
func (c *commentRepository) toDomain(daoComment dao.Comment) domain.Comment {
	comment := domain.Comment{
		Id: daoComment.Id,
		Commentator: domain.User{
			ID: daoComment.Uid,
		},
		Biz:     daoComment.Biz,
		BizId:   daoComment.BizId,
		Content: daoComment.Content,
		CTime:   time.UnixMilli(daoComment.Ctime),
	}
	if daoComment.PID.Valid {
		comment.ParentComment = &domain.Comment{
			Id: daoComment.PID.Int64,
		}
	}
	if daoComment.PID.Valid {
		comment.RootComment = &domain.Comment{
			Id: daoComment.PID.Int64,
		}
	}
	return comment
}
func (c *commentRepository) toEntity(domainComment domain.Comment) dao.Comment {
	daoComment := dao.Comment{
		Id:      domainComment.Id,
		Uid:     domainComment.Commentator.ID,
		Biz:     domainComment.Biz,
		BizId:   domainComment.BizId,
		Content: domainComment.Content,
	}
	if domainComment.RootComment != nil {
		daoComment.RootID = sql.NullInt64{
			Valid: true,
			Int64: domainComment.RootComment.Id,
		}
	}
	if domainComment.ParentComment != nil {
		daoComment.PID = sql.NullInt64{
			Valid: true,
			Int64: domainComment.ParentComment.Id,
		}
	}
	return daoComment
}
